package com.uxin.cameratest.gles;

import android.graphics.PointF;
import android.opengl.GLES20;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.LinkedList;

public class DrawTexture {
    public static final String VERTEX_SHADER =
            "attribute vec4 aPosition;\n" +
                    "attribute vec4 aTextureCoords;\n" +
                    "varying vec2 textureCoordinate;\n" +
                    "void main(){\n" +
                    "    gl_Position =  vec4(aPosition.xy,0.0,1.0);\n" +
                    "    textureCoordinate = aTextureCoords.st;\n" +
                    "}";

    public static final String FRAGMENT_SHADER =
            "precision mediump float;\n" +
                    "varying vec2 textureCoordinate;\n" +
                    "uniform sampler2D inputTexture;\n" +
                    "void main(){\n" +
                    "    gl_FragColor =texture2D(inputTexture,textureCoordinate);\n" +
                    "}";

    public static final float CUBE[] = {
            -1.0f, -1.0f,
            1.0f, -1.0f,
            -1.0f, 1.0f,
            1.0f, 1.0f,
    };

    public static final float TEXTURE_NO_ROTATION[] = {
            0.0f, 1.0f,
            1.0f, 1.0f,
            0.0f, 0.0f,
            1.0f, 0.0f,
    };

    public static final byte Indices[] ={
            0,1,2,1,3,2
    };


    protected final FloatBuffer mCubeBuffer;
    protected final FloatBuffer mTextureBuffer;
    protected final ByteBuffer mIndicesBuffer;

    private final LinkedList<Runnable> mRunOnDraw;
    //着色器代码
    private String mVertexShader;
    private String mFragmentShader;
    //作色器程序handle
    protected int mGLProgram = 0;
    protected int mPositionLoc = 0;
    protected int mTexCoordinateLoc = 0;
    protected int mInputTextureLoc;
    // 渲染图像Image的宽高
    protected int mInputWidth;
    protected int mInputHeight;
    //是否初始化
    protected boolean mIsInitialized;


    public DrawTexture(){
        this(VERTEX_SHADER,FRAGMENT_SHADER);
    }

    /**
     * 构造函数可以在非openGL线程中调用
     * @param vertexShader vertexShader
     * @param fragmentShader fragmentShader
     */
    public DrawTexture(final String vertexShader ,final String fragmentShader){
        mCubeBuffer = ByteBuffer.allocateDirect(CUBE.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        mCubeBuffer.put(CUBE).position(0);

        mTextureBuffer = ByteBuffer.allocateDirect(TEXTURE_NO_ROTATION.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        mTextureBuffer.put(TextureRotationUtil.getRotation(Rotation.NORMAL, false, true)).position(0);


        mIndicesBuffer = ByteBuffer.allocateDirect(Indices.length).order(ByteOrder.nativeOrder());
        mIndicesBuffer.put(TextureRotationUtil.Indices).position(0);


        mRunOnDraw = new LinkedList<>();
        mVertexShader =  vertexShader;
        mFragmentShader = fragmentShader;
    }


    /**
     * init函数必须要在openGL环境准备完成之后调用
     * 在本app里是在GLsurfaceView的onSurfaceCreate方法里调用的
     */
    public void init(){
        onInit();
        mIsInitialized = true;
    }

    protected void onInit() {
        mGLProgram = GLESUtils.createGLProgram(mVertexShader, mFragmentShader);
        if(mGLProgram == 0){
            throw new RuntimeException("Unable to create program");
        }
        mPositionLoc = GLES20.glGetAttribLocation(mGLProgram, "aPosition");
        mTexCoordinateLoc = GLES20.glGetAttribLocation(mGLProgram, "aTextureCoords");
        mInputTextureLoc = GLES20.glGetUniformLocation(mGLProgram, "inputTexture");
        int ttt = 12;
        ttt ++;
    }


    public void onInputSizeChanged(int width, int height) {
        mInputWidth = width;
        mInputHeight = height;
    }

    /**
     * 绘制纹理
     * @param textureId mTextureId
     * @return 0 success ,otherwise return -1
     */
    public int drawFrame(int textureId) {
        //使用着色器程序
        GLES20.glUseProgram(mGLProgram);
        runPendingOnDrawTasks();
        if(!mIsInitialized) {
            return GLESUtils.NOT_INIT;
        }

        //将顶点坐标缓冲数据传递到GPU
        mCubeBuffer.position(0);
        GLES20.glVertexAttribPointer(mPositionLoc, 2, GLES20.GL_FLOAT, false, GLESUtils.SIZEOF_FLOAT * 2, mCubeBuffer);
        GLES20.glEnableVertexAttribArray(mPositionLoc);


        //将纹理左边缓冲传递给GPU
        mTextureBuffer.position(0);
        GLES20.glVertexAttribPointer(mTexCoordinateLoc, 2, GLES20.GL_FLOAT, false, GLESUtils.SIZEOF_FLOAT * 2, mTextureBuffer);
        GLES20.glEnableVertexAttribArray(mTexCoordinateLoc);

        //开始绘制纹理
        if (textureId != GLESUtils.NO_TEXTURE) {
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
            GLES20.glUniform1i(mInputTextureLoc, 0);
            GLES20.glDrawElements(GLES20.GL_TRIANGLES, mIndicesBuffer.capacity(), GLES20.GL_UNSIGNED_BYTE, mIndicesBuffer);
        }

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        GLES20.glDisableVertexAttribArray(mPositionLoc);
        GLES20.glDisableVertexAttribArray(mTexCoordinateLoc);
        GLES20.glUseProgram(0);

        return 0;
    }

    public int getGLProgram(){
        return mGLProgram;
    }



    protected void runPendingOnDrawTasks() {
        while (!mRunOnDraw.isEmpty()) {
            mRunOnDraw.removeFirst().run();
        }
    }

    public boolean isInitialized(){
        return mIsInitialized;
    }

    protected void setInteger(final int location, final int intValue) {
        runOnDraw(new Runnable() {
            @Override
            public void run() {
                GLES20.glUniform1i(location, intValue);
            }
        });
    }

    public void setFloat(final int location, final float floatValue) {
        runOnDraw(new Runnable() {
            @Override
            public void run() {
                GLES20.glUniform1f(location, floatValue);
            }
        });
    }

    protected void setFloatVec2(final int location, final float[] arrayValue) {
        runOnDraw(new Runnable() {
            @Override
            public void run() {
                GLES20.glUniform2fv(location, 1, FloatBuffer.wrap(arrayValue));
            }
        });
    }

    protected void setFloatVec3(final int location, final float[] arrayValue) {
        runOnDraw(new Runnable() {
            @Override
            public void run() {
                GLES20.glUniform3fv(location, 1, FloatBuffer.wrap(arrayValue));
            }
        });
    }

    protected void setFloatVec4(final int location, final float[] arrayValue) {
        runOnDraw(new Runnable() {
            @Override
            public void run() {
                GLES20.glUniform4fv(location, 1, FloatBuffer.wrap(arrayValue));
            }
        });
    }

    protected void setFloatArray(final int location, final float[] arrayValue) {
        runOnDraw(new Runnable() {
            @Override
            public void run() {
                GLES20.glUniform1fv(location, arrayValue.length, FloatBuffer.wrap(arrayValue));
            }
        });
    }

    protected void setPoint(final int location, final PointF point) {
        runOnDraw(new Runnable() {
            @Override
            public void run() {
                float[] vec2 = new float[2];
                vec2[0] = point.x;
                vec2[1] = point.y;
                GLES20.glUniform2fv(location, 1, vec2, 0);
            }
        });
    }

    protected void setUniformMatrix3f(final int location, final float[] matrix) {
        runOnDraw(new Runnable() {

            @Override
            public void run() {
                GLES20.glUniformMatrix3fv(location, 1, false, matrix, 0);
            }
        });
    }

    protected void setUniformMatrix4f(final int location, final float[] matrix) {
        runOnDraw(new Runnable() {
            @Override
            public void run() {
                GLES20.glUniformMatrix4fv(location, 1, false, matrix, 0);
            }
        });
    }

    protected void runOnDraw(final Runnable runnable) {
        synchronized (mRunOnDraw) {
            mRunOnDraw.addLast(runnable);
        }
    }


    /**
     * 渲染程序退出之后,必须要在openGL线程中调用
     */
    public void destroy() {
        mIsInitialized = false;
        GLES20.glDeleteProgram(mGLProgram);
        mGLProgram = -1;
        if(mCubeBuffer != null){
            mCubeBuffer.clear();
        }
        if(mTextureBuffer != null){
            mTextureBuffer.clear();
        }
        if(mIndicesBuffer != null){
            mIndicesBuffer.clear();
        }

        synchronized (mRunOnDraw){
            mRunOnDraw.clear();
        }

        onDestroy();
    }

    protected void onDestroy() {

    }
}
